package znet

import (
	"errors"
	"fmt"
	"io"
	"net"
	"sync"
	"zinx/utils"
	"zinx/ziface"
)

// 链接模块
type Connection struct {
	// 当前Conn 隶属于哪个Server
	TcpServer ziface.Isever
	// 当前链接的socket TCP 套接字
	Conn *net.TCPConn
	// 链接ID
	ConnID uint32
	// 当前链接状态
	isClosed bool
	//告知当前链接已经退出/停止的channel(由Reader告知Writer退出)
	ExitChan chan bool
	// 无缓冲的管道，用于读和写goroutine之间的消息通信
	msgChan chan []byte
	// 消息的管理MsgId和对应处理业务的API关系
	MsgHandler ziface.IMsgHandler

	// 链接属性的集合
	property map[string]interface{}
	//保护链接属性的锁
	propertyLock sync.RWMutex
}

// 初始化链接模块的方法
func NewConnection(server ziface.Isever, conn *net.TCPConn, connID uint32, handler ziface.IMsgHandler) *Connection {
	c := &Connection{
		TcpServer:  server,
		Conn:       conn,
		ConnID:     connID,
		MsgHandler: handler,
		isClosed:   false,
		msgChan:    make(chan []byte),
		ExitChan:   make(chan bool, 1),
		property:   make(map[string]interface{}),
	}
	// 将conn 加入到ConnManager
	c.TcpServer.GetConnMgr().Add(c)
	return c
}

// 链接的读业务方法
func (c *Connection) StartReader() {
	fmt.Println("[Read Goroutine is running]")
	defer fmt.Println("connID = ", c.ConnID, " [Reader is exit], remote addr is", c.RemoteAddr().String())
	defer c.Stop()

	for {
		// 读取客户端的数据到buff中，最大512字节
		//buff := make([]byte, utils.GlobalObject.MaxPackageSize)
		//_, err := c.Conn.Read(buff)
		//if err != nil {
		//	fmt.Println("recv buff err: ", err)
		//	continue
		//}
		// 创建一个拆包解包的对象
		dp := NewDataPack()
		// 读取客户端的msg head 二进制流 8个字节
		headData := make([]byte, dp.GetHeadLen())
		if _, err := io.ReadFull(c.GetTCPConnection(), headData); err != nil {
			fmt.Println("read msg head error: ", err)
			break
		}

		// 拆包，得到msg id 和 msgDataLen 放到msg结构体中
		msg, err := dp.Unpack(headData)
		if err != nil {
			fmt.Println("unpack error ", err)
			break
		}
		// 根据dataLen 再次读取Data， 放到msg.Data中
		var data []byte
		if msg.GetDataLen() > 0 {
			data = make([]byte, msg.GetDataLen())
			if _, err := io.ReadFull(c.GetTCPConnection(), data); err != nil {
				fmt.Println("read msg data error: ", err)
				break
			}
		}
		msg.SetData(data)
		// 得到当前的conn数据的request请求数据
		req := Request{
			conn: c,
			msg:  msg,
		}
		// 判断是否开启工作池机制
		if utils.GlobalObject.WorkerPoolSize > 0 {
			// 已经开启了工作池机制，将消息发送给worker工作池处理
			c.MsgHandler.SendMsgToTaskQueue(&req)
		} else {
			// 从路由中主找到注册绑定的Conn对应的router调用
			// 根据绑定好的额MsgId 找到对应处理api业务执行
			go c.MsgHandler.DoMsgHandler(&req)
		}
	}
}

// 写消息gorotine, 专门发送给客户消息的模块
func (c *Connection) StartWrite() {
	fmt.Println("[Write Goroutine is running]")
	defer fmt.Println("[conn writer exit!]", c.RemoteAddr().String())

	// 不断的阻塞的等待channel的消息，进行写给客户端
	for {
		select {
		case data := <-c.msgChan:
			if _, err := c.Conn.Write(data); err != nil {
				fmt.Println("Send data error: ", err)
			}
		case <-c.ExitChan:
			// 代表Reader已经退出了，此时Writer也应该退出
			return
		}
	}
}

// 启动链接 让当前的链接准备开始工作
func (c *Connection) Start() {
	fmt.Println("Conn start....ConnID = ", c.ConnID)
	//启动从当前链接的读取数据的业务
	go c.StartReader()
	// 启动从当前链接写数据到业务
	go c.StartWrite()
	// 按照开发者传递过来的 创建之后需要调用的处理业务， 执行对应的hook函数
	c.TcpServer.CallOnConnStart(c)
}

// 停止链接 结束当前链接的工作
func (c *Connection) Stop() {
	fmt.Println("Conn Stop....ConnID = ", c.ConnID)

	// 如果当前链接已经关闭
	if c.isClosed {
		return
	}
	c.isClosed = true

	// 调用开发者注册的 销毁链接之前 需要执行的业务的hook函数
	c.TcpServer.CallOnConnStop(c)

	// 关闭socket链接
	c.Conn.Close()
	//告知writer退出
	c.ExitChan <- true
	// 将当前链接从connMgr中移除掉
	c.TcpServer.GetConnMgr().Remove(c)
	// 回收资源
	close(c.ExitChan)
	close(c.msgChan)
}

// 获取当前链接的绑定tcp conn
func (c *Connection) GetTCPConnection() *net.TCPConn {
	return c.Conn
}

// 获取当前链接模块的链接ID
func (c *Connection) GetConnID() uint32 {
	return c.ConnID
}

// 获取远程客户端的TCP状态 IP 端口
func (c *Connection) RemoteAddr() net.Addr {
	return c.Conn.RemoteAddr()
}

// 提供一个SendMsg 方法 将我们要发送给客户的数据，先封包，再发送
func (c *Connection) SendMsg(msgId uint32, data []byte) error {
	if c.isClosed {
		return errors.New("Connection closed when send msg")
	}

	// 将data进行风暴 msgDataLen msgId
	dp := NewDataPack()

	binaryMsg, err := dp.Pack(NewMsgPackage(msgId, data))
	if err != nil {
		fmt.Println("Pack eroor msg id = ", msgId)
		return errors.New("Pack error msg")
	}

	// 将数据发送给客户端
	c.msgChan <- binaryMsg

	return nil
}

// 设置链接属性
func (c *Connection) SetProperty(key string, value interface{}) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()
	c.property[key] = value
}

// 获取链接属性
func (c *Connection) GetProperty(key string) (interface{}, error) {
	c.propertyLock.RLock()
	defer c.propertyLock.RUnlock()
	if value, ok := c.property[key]; ok {
		return value, nil
	}
	return nil, errors.New("no property found")
}

// 移除链接属性
func (c *Connection) RemoveProperty(key string) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()
	delete(c.property, key)
}
